package util;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

public class Logger {
    private HashMap<String, ArrayList<Double>> perUpdateTime = new HashMap<>();
    private HashMap<String, HashSet<String>> verificationReports = new HashMap<>();

    public int ruleCnt = 0;

    public void addPerUpdateTime(String name, double time) {
        if (!perUpdateTime.containsKey(name)) {
            perUpdateTime.put(name, new ArrayList<>());
        }
        perUpdateTime.get(name).add(time);
    }

    public void displayAllTime() {
        double ttot = 0;
        boolean firstTime = true;

        for (Map.Entry<String, ArrayList<Double>> it : perUpdateTime.entrySet()) {
            if (firstTime) {
                System.out.println(it.getValue().size() + " updates in total.");
                firstTime = false;
            }

            ArrayList<Double> times = it.getValue();
            Collections.sort(times);

            double tot = 0, mean;
            for (double t : times ) tot += t;
            if (times.size() % 2 == 0) {
                mean = (times.get(times.size() / 2) + times.get(times.size() / 2 + 1)) / 2;
            } else {
                mean = times.get(times.size() / 2);
            }
            tot /= it.getValue().size();
            ttot += tot;
            System.out.println(it.getKey() + ": " + tot + " us per update with (min, mean, max) = (" + times.get(0) + ", " + mean + ", " + times.get(times.size() - 1) + ").");
        }
        System.out.println("    Total " + ttot + " us per update");
    }

    public void printAllTime(String filename) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
        for(Map.Entry<String, ArrayList<Double>> it : perUpdateTime.entrySet()) {
            bw.write(it.getKey());
            bw.newLine();
            boolean first = true;
            for (double t : it.getValue()) {
                if (first) {
                    first = false;
                } else {
                    bw.write(" ");
                }
                bw.write("" + t);
            }
            bw.newLine();
        }
        bw.close();
    }

    public void printSumTime(String filename, double div) throws IOException {
        double t = 0;
        for (Map.Entry<String, ArrayList<Double>> it : perUpdateTime.entrySet()) {
            for (Double tt : it.getValue()) {
                t += tt;
            }
        }

        BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
        bw.write("" + t / div);
        bw.close();
        return;
    }

    public void printTotalTime(String filename, int num, int size) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
        for (Map.Entry<String, ArrayList<Double>> it : perUpdateTime.entrySet()) {
            int length = it.getValue().size();
            boolean first = true;
            for (int i = length - num * size; i < length; i += size) {
                double t = 0;
                for (ArrayList<Double> values : perUpdateTime.values()) {
                    for (int j = i; j < i + size; j ++) {
                        t += values.get(j);
                    }
                }

                if (first) {
                    first = false;
                } else {
                    bw.write(" ");
                }
                bw.write("" + t / size);
            }
            bw.close();
            return;
        }
    }

    public void addVerificationReports(String verifierName, String ErrorName, String DeviceName) {
        if (ErrorName.contentEquals("Blackhole")) {
            // FIXME: decide a consistent definition of blackhole
            return;
        }

        if (!verificationReports.containsKey(verifierName)) {
            verificationReports.put(verifierName, new HashSet<>());
        }
        verificationReports.get(verifierName).add(ErrorName + " at " + DeviceName);
    }

    public boolean checkReportsConsistency() {
        HashSet<String> firstReports = null;
        String firstVerifierName = null;

        for(Map.Entry<String, HashSet<String>> it : verificationReports.entrySet())
            if (firstReports == null) {
                firstReports = it.getValue();
                firstVerifierName = it.getKey();
            } else {
                for (String report : it.getValue())
                    if (!firstReports.contains(report)) {
                        System.out.println(it.getKey() + " has " + report + " that is not in " + firstVerifierName);
                        return false;
                    }
                for (String report : firstReports)
                    if (!it.getValue().contains(report)) {
                        System.out.println(firstVerifierName + " has " + report + " that is not in " + it.getKey());
                        return false;
                    }
            }

        return true;
    }
}